package org.chs.ts.batch.factory;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;

import lombok.extern.slf4j.Slf4j;
import org.chs.ts.base.batch.del.entity.BatchClearBasicTaskExecDel;
import org.chs.ts.base.batch.del.entity.BatchClearBasicTaskStepExecDel;
import org.chs.ts.base.batch.exec.entity.BatchClearBasicTaskExec;
import org.chs.ts.base.batch.exec.entity.BatchClearBasicTaskStepExec;
import org.chs.ts.base.batch.exec.entity.BatchDailyBasicTaskStepExec;
import org.chs.ts.base.batch.set.entity.BatchClearBasicTaskSet;
import org.chs.ts.base.batch.set.entity.BatchClearBasicTaskStepSet;
import org.chs.ts.base.batch.set.entity.BatchDailyBasicTaskPublish;
import org.chs.ts.base.batch.vo.entity.BatchDailyExecReq;
import org.chs.ts.base.batch.vo.entity.BatchExecReq;
import org.chs.ts.base.common.util.XssFilterUtils;
import org.chs.ts.base.constant.CommonConstant;
import org.chs.ts.batch.constant.TaskDailyStatusCodeEnum;
import org.chs.ts.batch.constant.TaskStatusCodeEnum;
import org.chs.ts.batch.service.*;
import org.chs.ts.batch.util.*;

import org.chs.ts.batch.exception.TaskDailyException;
import org.chs.ts.batch.exception.TaskExecException;

import org.chs.ts.batch.thread.BatchThreadPool;

import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Compay 盛唐天行
 * @Author: wuwei
 * @Censor By wuwei
 * @Sence 2023/1/5 16:56
 * @Modify 2023/1/5 16:56
 * @Description
 */
@Slf4j
@Service
public class TaskExecFactory {


    @Resource
    RedisTemplate redisTemplate;

    @Resource
    private ParameterUtil parameterUtil;

    @Resource
    private TaskBuilder taskBuilder;

    @Resource
    private IBatchClearBasicTaskExecService clearBasicTaskExecService;

    @Resource
    private IBatchClearBasicTaskStepExecService clearBasicTaskStepExecService;

    @Resource
    private IBatchClearBasicTaskExecDelService clearBasicTaskExecDelService;

    @Resource
    private IBatchClearBasicTaskStepExecDelService clearBasicTaskStepExecDelService;

    @Resource
    private IBatchClearBasicTaskSetService clearBasicTaskSetService;

    @Resource
    private IBatchClearBasicTaskStepSetService clearBasicTaskStepSetService;

    @Resource
    private IBatchDailyBasicTaskPublishService dailyBasicTaskPublishService;

    @Resource
    private IBatchDailyBasicTaskStepSetService dailyBasicTaskStepSetService;

    @Resource
    private IBatchDailyBasicTaskStepExecService dailyBasicTaskStepExecService;




    // redis default namespace
    //@Value(value = "${batch.redis-properties.redis-task-namespace:task:}")
    private final String REDIS_TASK_NAMESPACE = "batch:";

    // redis 线程锁 获取最大次数
    //@Value(value = "${batch.redis-properties.redis-get-lock-max-num:3}")
    private final String REDIS_GET_LOCK_MAX_NUM_KEY = REDIS_TASK_NAMESPACE + "batch.redis-properties.redis-get-lock-max-num";
    private int REDIS_GET_LOCK_MAX_NUM = 3;

    // redis 线程默认 超时时间
    //@Value(value = "${batch.redis-properties.redis-value-expire:12}")
    private final String REDIS_VALUE_EXPIRE_KEY = REDIS_TASK_NAMESPACE + "batch.redis-properties.redis-value-expire";
    private int REDIS_VALUE_EXPIRE = 12;

    // redis 线程默认 超时时间单位
    private final TimeUnit REDIS_VALUE_EXPIRE_TIMEUNIT = TimeUnit.HOURS;

    // redis task_all_list_[taskGroup]_[execDate] 分布式全局锁 默认超时时间
    //@Value(value = "${batch.redis-properties.redis-task-list-expire:2}")
    private final String REDIS_TASK_LIST_EXPIRE_KEY = REDIS_TASK_NAMESPACE + "batch.redis-properties.redis-task-list-expire";
    private int REDIS_TASK_LIST_EXPIRE = 2;

    // redis task_all_list_[taskGroup]_[execDate] 分布式全局锁 默认超时时间单位
    private final TimeUnit REDIS_TASK_LIST_EXPIRE_TIMEUNIT = TimeUnit.MINUTES;


    // 任务默认占用时长 单位毫秒
    //@Value(value = "${batch.redis-properties.redis-task-running-max-overtime:8000}")
    private final String REDIS_TASK_RUNNING_MAX_OVERTIME_KEY = REDIS_TASK_NAMESPACE + "batch.redis-properties.redis-task-running-max-overtime";
    private long REDIS_TASK_RUNNING_MAX_OVERTIME = 300000;

    @PostConstruct
    private void initParameter() {
        log.info("*********  TaskExecFactory ********* 初始化 跑批redis 控制开始参数 *********");
        initREDIS_GET_LOCK_MAX_NUM(true);
        initREDIS_VALUE_EXPIRE(true);
        initREDIS_TASK_LIST_EXPIRE(true);
        initREDIS_TASK_RUNNING_MAX_OVERTIME(true);
        log.info("*********  TaskExecFactory ********* 初始化 跑批redis 控制结束参数 *********");
    }

    private void initREDIS_GET_LOCK_MAX_NUM(boolean isInit) {
        boolean REDIS_GET_LOCK_MAX_NUM_isDefault =
                "".equals(parameterUtil.getParameterByCode(REDIS_GET_LOCK_MAX_NUM_KEY)) || !parameterUtil.getParameterByCode(REDIS_GET_LOCK_MAX_NUM_KEY).matches("\\d+");
        REDIS_GET_LOCK_MAX_NUM = Integer.parseInt(!REDIS_GET_LOCK_MAX_NUM_isDefault ? parameterUtil.getParameterByCode(REDIS_GET_LOCK_MAX_NUM_KEY)
                : REDIS_GET_LOCK_MAX_NUM + "");
        if (REDIS_GET_LOCK_MAX_NUM_isDefault || isInit) {
            log.info("REDIS_GET_LOCK_MAX_NUM : {} {}", REDIS_GET_LOCK_MAX_NUM, (REDIS_GET_LOCK_MAX_NUM_isDefault ?
                    "[系统参数[" + REDIS_GET_LOCK_MAX_NUM_KEY + "]未配置或不合法，取用默认值]" : ""));
        }
    }

    private void initREDIS_VALUE_EXPIRE(boolean isInit) {
        boolean REDIS_VALUE_EXPIRE_isDefault =
                "".equals(parameterUtil.getParameterByCode(REDIS_VALUE_EXPIRE_KEY)) || !parameterUtil.getParameterByCode(REDIS_VALUE_EXPIRE_KEY).matches("\\d+");
        REDIS_VALUE_EXPIRE = Integer.parseInt(!REDIS_VALUE_EXPIRE_isDefault ? parameterUtil.getParameterByCode(REDIS_VALUE_EXPIRE_KEY) :
                REDIS_VALUE_EXPIRE + "");
        if (REDIS_VALUE_EXPIRE_isDefault || isInit) {
            log.info("REDIS_VALUE_EXPIRE : {} {}", REDIS_VALUE_EXPIRE, (REDIS_VALUE_EXPIRE_isDefault ? "[系统参数[" + REDIS_VALUE_EXPIRE_KEY +
                    "]未配置或不合法，取用默认值]" : ""));
        }
    }

    private void initREDIS_TASK_LIST_EXPIRE(boolean isInit) {
        boolean REDIS_TASK_LIST_EXPIRE_isDefault =
                "".equals(parameterUtil.getParameterByCode(REDIS_TASK_LIST_EXPIRE_KEY)) || !parameterUtil.getParameterByCode(REDIS_TASK_LIST_EXPIRE_KEY).matches("\\d+");
        REDIS_TASK_LIST_EXPIRE = Integer.parseInt(!REDIS_TASK_LIST_EXPIRE_isDefault ? parameterUtil.getParameterByCode(REDIS_TASK_LIST_EXPIRE_KEY)
                : REDIS_TASK_LIST_EXPIRE + "");
        if (REDIS_TASK_LIST_EXPIRE_isDefault || isInit) {
            log.info("REDIS_TASK_LIST_EXPIRE : {} {}", REDIS_TASK_LIST_EXPIRE, (REDIS_TASK_LIST_EXPIRE_isDefault ?
                    "[系统参数[" + REDIS_TASK_LIST_EXPIRE_KEY + "]未配置或不合法，取用默认值]" : ""));
        }
    }

    private void initREDIS_TASK_RUNNING_MAX_OVERTIME(boolean isInit) {
        boolean REDIS_TASK_RUNNING_MAX_OVERTIME_isDefault =
                "".equals(parameterUtil.getParameterByCode(REDIS_TASK_RUNNING_MAX_OVERTIME_KEY)) || !parameterUtil.getParameterByCode(REDIS_TASK_RUNNING_MAX_OVERTIME_KEY).matches("\\d+");
        REDIS_TASK_RUNNING_MAX_OVERTIME = Integer.parseInt(!REDIS_TASK_RUNNING_MAX_OVERTIME_isDefault ?
                parameterUtil.getParameterByCode(REDIS_TASK_RUNNING_MAX_OVERTIME_KEY) : REDIS_TASK_RUNNING_MAX_OVERTIME + "");
        if (REDIS_TASK_RUNNING_MAX_OVERTIME_isDefault || isInit) {
            log.info("REDIS_TASK_RUNNING_MAX_OVERTIME : {} {}", REDIS_TASK_RUNNING_MAX_OVERTIME, (REDIS_TASK_RUNNING_MAX_OVERTIME_isDefault ?
                    "[系统参数[" + REDIS_TASK_RUNNING_MAX_OVERTIME_KEY + "]未配置或不合法，取用默认值]" : ""));
        }
    }


    private String getGlobalLockKey(String batchSysCode, String taskGroup, String execDate) {
        return REDIS_TASK_NAMESPACE + "lock_" + "task_all_list_" + batchSysCode + "_" + taskGroup + "_" + execDate;
    }

    private String getDailyGlobalLockKey(String batchSysCode, String execDate) {
        return REDIS_TASK_NAMESPACE + "lock_" + "task_daily_list_" + batchSysCode + "_" + execDate;
    }

    public String getTaskListKey(String batchSysCode, String taskGroup, String execDate) {
        return REDIS_TASK_NAMESPACE + "task_all_list_" + batchSysCode + "_" + taskGroup + "_" + execDate;

    }

    private String getTaskLockKey(String batchSysCode, String taskGroup, String execDate, String taskExecid) {
        return REDIS_TASK_NAMESPACE + "task_get_" + batchSysCode + "_" + taskGroup + "_" + execDate + "_" + taskExecid;
    }

    private String getTaskDailyLockKey(String batchSysCode, String execDate, String taskExecid) {
        return REDIS_TASK_NAMESPACE + "task_get_daily_" + batchSysCode + "_" + execDate + "_" + taskExecid;
    }

    private String getTaskRunningKey(String batchSysCode, String taskGroup, String execDate) {
        return REDIS_TASK_NAMESPACE + "task_running_" + batchSysCode + "_" + taskGroup + "_" + execDate;
    }

    private String getTaskWaittingKey(String batchSysCode) {
        return REDIS_TASK_NAMESPACE + "task_waitting_" + batchSysCode;
    }


    /**
     * 根据 当前 清算任务配置执行表列表 执行状态为 0:未执行 2:执行中 4:执行失败 返回可争抢列表
     *
     * @param taskExecList 清算任务配置执行表列表 (执行状态为 0:未执行 2:执行中 4:执行失败)
     * @return 返回可争抢清算任务配置执行表列表
     * @throws TaskExecException 自定义清算任务异常对象
     */
    private List<BatchClearBasicTaskExec> getAvailableTaskExec(List<BatchClearBasicTaskExec> taskExecList) throws TaskExecException {
        if (taskExecList.isEmpty())
            throw new TaskExecException("BatchClearBasicTaskExec 不存在 execStatus='0' 任务信息！ ", TaskStatusCodeEnum.TASK_GETTASK_ERROR, "");

        // 0、确认传递列表数量 如果 等于1 确认 execStatus='0' 并进行返回
        if (taskExecList.size() == 1)
            return taskExecList.stream().filter(item -> item.getExecStatus().equals("0")).collect(Collectors.toList());
        // 1、判断返回当前列表 是否 存在 execStatus='4' 执行失败 如果存在 只返回 执行失败的同级的
        List<BatchClearBasicTaskExec> tempList = taskExecList.stream().filter(item -> item.getExecStatus().equals("4")).collect(Collectors.toList());
        // 存在 执行失败 只返回 执行失败的同级
        if (!tempList.isEmpty()) {
            // 验证 是否存在 跨级 失败 即 execStatus='4' exec_order 不同
            // 对结果集 进行分组验证是否 存在 exec_order 不同
            Map<Integer, List<BatchClearBasicTaskExec>> collect =
                    taskExecList.stream().filter(item -> item.getExecStatus().equals("4")).collect(Collectors.groupingBy(BatchClearBasicTaskExec::getExecOrder));
            if (!collect.isEmpty()) { // 存在 execStatus='4' exec_order 不同 执行异常 返回空
                throw new TaskExecException("BatchClearBasicTaskExec 存在 execStatus='4' exec_order 不同 执行异常",
                        TaskStatusCodeEnum.TASK_GETTASK_NULL_SUCCESS, "");
            }
            // 获取 returnList 结果集中 exec_order 只有一个值  任意对象的 exec_order
            Integer execOrderTemp = tempList.get(0).getExecOrder();
            return taskExecList.stream().filter(item -> item.getExecStatus().equals("0") && item.getExecOrder().equals(execOrderTemp)).collect(Collectors.toList());
        }

        // 2、判断返回当前列表 是否存在 execStatus='2' or execStatus='1' 执行中 如果存在 只返回失败的同级
        tempList =
                taskExecList.stream().filter(item -> item.getExecStatus().equals("2") || item.getExecStatus().equals("1")).collect(Collectors.toList());
        // 存在 执行中 只返回 同级待执行
        if (!tempList.isEmpty()) {
            // 验证 是否存在 跨级 失败 即 execStatus='2' or execStatus='1' exec_order 不同
            // 对结果集 进行分组验证是否 存在 exec_order 不同
            Map<Integer, List<BatchClearBasicTaskExec>> collect =
                    taskExecList.stream().filter(item -> item.getExecStatus().equals("2") || item.getExecStatus().equals("1")).collect(Collectors.groupingBy(BatchClearBasicTaskExec::getExecOrder));
            if (collect.size() > 1) { // 存在 execStatus='2' exec_order 不同 执行异常 返回空
                throw new TaskExecException("BatchClearBasicTaskExec 存在 execStatus='2' or execStatus='1' exec_order 不同 执行异常",
                        TaskStatusCodeEnum.TASK_GETTASK_NULL_SUCCESS, "");
            }
            // 获取 returnList 结果集中 exec_order 只有一个值  任意对象的 exec_order
            Integer execOrderTemp = tempList.get(0).getExecOrder();
            return taskExecList.stream().filter(item -> item.getExecStatus().equals("0") && item.getExecOrder().equals(execOrderTemp)).collect(Collectors.toList());
        }

        // 3、直接返回 execStatus='0'  exec_order最高级别
        Map<Integer, List<BatchClearBasicTaskExec>> collect = taskExecList.stream().filter(item -> item.getExecStatus().equals("0"))
                .collect(Collectors.groupingBy(BatchClearBasicTaskExec::getExecOrder));
        return collect.get(collect.keySet().stream().sorted().collect(Collectors.toList()).get(0));
    }

    /**
     * @param batchSysCode 跑批系统代码
     * @param taskGroup    跑批组id
     * @param execDate     跑批日期
     * @param taskExecid   跑批执行id
     */
    public BatchClearBasicTaskExec doTaskExec(String batchSysCode, String taskGroup, String execDate, String taskExecid, String snatchingMode) throws TaskExecException {
        // 确认 任务有没有被抢夺
        if (Boolean.FALSE.equals(redisTemplate.hasKey(getTaskLockKey(batchSysCode, taskGroup, execDate, taskExecid)))) {
            throw new TaskExecException("doTaskExec -> 获取redis task_get_" + batchSysCode + "_" + taskGroup + "_" + execDate + "_" + taskExecid + " " +
                    "失败。未获取到指定redis锁！", TaskStatusCodeEnum.TASK_RUNNING_RUNNINGERROR, taskExecid);

        }
        // 更新 database
        BatchClearBasicTaskExec obj = clearBasicTaskExecService.getOne(new LambdaQueryWrapper<BatchClearBasicTaskExec>()
                .eq(BatchClearBasicTaskExec::getTaskExecid, taskExecid));
        if (obj == null) {
            throw new TaskExecException("doTaskExec -> 获取 batchClearBasicTaskExec 失败！未查询到 task_exec_id [" + taskExecid + "] 的信息",
                    TaskStatusCodeEnum.TASK_RUNNING_RUNNINGERROR, taskExecid);

        }
        obj.setExecStatus("2");
        String errMsg;
        try {
            clearBasicTaskExecService.updateByObject(obj);
        } catch (Exception e) {
            errMsg = "batchClearBasicTaskExecService updateByObject:" + obj.getTaskExecid();
            log.error(errMsg);
            throw new TaskExecException(errMsg, TaskStatusCodeEnum.TASK_RUNNING_RUNNINGERROR, obj.getTaskExecid());

        }
        if (snatchingMode.equals("redis")) {
            // 更新 redis 执行列表 对象状态
            updateRedisListValue(obj);
        }
        // 删除 redis
        delRedis(getGlobalLockKey(batchSysCode, taskGroup, execDate), getTaskLockKey(batchSysCode, taskGroup, execDate, taskExecid));

        return obj;
    }


    /**
     * @param batchSysCode 跑批系统代码
     * @param execDate     跑批日期
     * @param taskExecid   跑批执行id
     */
    public BatchDailyBasicTaskStepExec doTaskDailyExec(String batchSysCode, String execDate, String taskExecid) throws TaskDailyException {
        // 确认 任务有没有被抢夺
        if (Boolean.FALSE.equals(redisTemplate.hasKey(getTaskDailyLockKey(batchSysCode, execDate, taskExecid)))) {
            throw new TaskDailyException("doTaskDailyExec -> 获取redis task_get_daily_" + batchSysCode + "_" + execDate + "_" + taskExecid + " " +
                    "失败。未获取到指定redis锁！", TaskDailyStatusCodeEnum.TASK_RUNNING_RUNNINGERROR, taskExecid);

        }
        // 更新 database
        BatchDailyBasicTaskStepExec obj = dailyBasicTaskStepExecService.getOne(new LambdaQueryWrapper<BatchDailyBasicTaskStepExec>()
                .eq(BatchDailyBasicTaskStepExec::getTaskExecid, taskExecid));
        if (obj == null) {
            throw new TaskDailyException("doTaskExec -> 获取 BatchDailyBasicTaskStepExec 失败！未查询到 task_exec_id [" + taskExecid + "] 的信息",
                    TaskDailyStatusCodeEnum.TASK_RUNNING_RUNNINGERROR, taskExecid);

        }
        obj.setExecStatus("2");
        String errMsg;
        try {
            dailyBasicTaskStepExecService.updateByObject(obj);
        } catch (Exception e) {
            errMsg = "dailyBasicTaskStepExecService updateByObject:" + obj.getTaskExecid();
            log.error(errMsg);
            throw new TaskDailyException(errMsg, TaskDailyStatusCodeEnum.TASK_RUNNING_RUNNINGERROR, obj.getTaskExecid());

        }

        // 删除 redis
        delRedis(getDailyGlobalLockKey(batchSysCode, execDate), getTaskDailyLockKey(batchSysCode, execDate, taskExecid));

        return obj;
    }


    /**
     * 根据 跑批任务 获取 跑批子任务
     */
    public List<BatchClearBasicTaskStepExec> getTaskStepExecListByTaskExec(BatchClearBasicTaskExec taskExec) throws TaskExecException {
        List<BatchClearBasicTaskStepExec> taskStepExecList = clearBasicTaskStepExecService.list(
                new LambdaQueryWrapper<BatchClearBasicTaskStepExec>()
                        .eq(BatchClearBasicTaskStepExec::getBatchSysCode, taskExec.getBatchSysCode())
                        .eq(BatchClearBasicTaskStepExec::getTaskGroup, taskExec.getTaskGroup())
                        .eq(BatchClearBasicTaskStepExec::getTaskId, taskExec.getTaskId())
                        .eq(BatchClearBasicTaskStepExec::getExecDate, taskExec.getExecDate())
        );

        if (taskStepExecList.isEmpty()) {
            throw new TaskExecException("getTaskStepExecListByTaskExec -> 获取 BatchClearBasicTaskStepExec 失败！未查询到 batch_sys_code [" + taskExec.getBatchSysCode() + "] task_group [" + taskExec.getTaskGroup() + "] task_id [" + taskExec.getTaskId() + "]  task_date [" + taskExec.getTaskDate() + "] 的信息", TaskStatusCodeEnum.TASK_RUNNING_RUNNINGERROR, taskExec.getTaskExecid());
        }

        return taskStepExecList;
    }


    /**
     * 针对 redis 存储 List数据 更新 List内相关数据(无分布式锁)
     *
     * @param clearBasicTaskExec 跑批执行对象
     * @param listKey            redis list缓存数据 key
     * @param taskList           list数据
     * @throws TaskExecException 跑批异常对象
     */
    private void updateRedisListValue(BatchClearBasicTaskExec clearBasicTaskExec, String listKey, List<BatchClearBasicTaskExec> taskList) throws TaskExecException {
        int index = -1;
        for (int i = 0; i < taskList.size(); i++) {
            if (taskList.get(i).getTaskExecid().equals(clearBasicTaskExec.getTaskExecid())) {
                index = i;
                break;
            }
        }
        if (index == -1) {
            // 未在redis 列表 查找到有效 pmcsClearBasicTaskExec 对象的下标。无法更新当前redis taskExecList
            // 处理方案：1、重置 redis taskExecList
            //         2、抛出异常 当前抢夺结束
            initTaskExecForRedis(clearBasicTaskExec.getBatchSysCode(), clearBasicTaskExec.getExecDate(), clearBasicTaskExec.getTaskGroup());
            throw new TaskExecException("抢夺失败,抢夺任务 batchSysCode[" + clearBasicTaskExec.getBatchSysCode() + "] taskExecid[" + clearBasicTaskExec.getTaskExecid() + "]未存在redis 列表中！", TaskStatusCodeEnum.TASK_GETTASK_RUNNINGERROR, "");

        }
        redisTemplate.opsForList().set(listKey, index, clearBasicTaskExec);
    }

    /**
     * 针对 redis 存储 List数据 更新 List内相关数据(有分布式锁)
     *
     * @param obj 跑批执行对象
     */
    public void updateRedisListValue(BatchClearBasicTaskExec obj) throws TaskExecException {
        String taskGroup = obj.getTaskGroup();
        String execDate = obj.getExecDate();
        String batchSysCode = obj.getBatchSysCode();
        boolean isInit = false;
        if (Boolean.TRUE.equals(redisTemplate.hasKey(getTaskListKey(batchSysCode, taskGroup, execDate)))) {
            String lockKey = getGlobalLockKey(batchSysCode, taskGroup, execDate);
            String listKey = getTaskListKey(batchSysCode, taskGroup, execDate);
            // 1、task_all_list_[execDate] -> 所有可执行任务
            if (getRedisLock(lockKey, "updateRedisListValue" + "_" + System.currentTimeMillis())) {
                try {
                    List<BatchClearBasicTaskExec> taskList = getRange(listKey);
                    int i = -1;
                    for (int j = 0; j < Objects.requireNonNull(taskList).size(); j++) {
                        if (taskList.get(j).getId().equals(obj.getId())) {
                            i = j;
                            break;
                        }
                    }

                    if (i == -1) {
                        isInit = true;
                    } else {
                        redisTemplate.opsForList().set(listKey, i, obj);
                    }

                    log.info("updateRedisListValue method finish@batchSysCode -> " + batchSysCode + "; execDate -> " + execDate + "; taskGroup -> " + taskGroup);

                } finally {
                    unRedisLock(lockKey);
                }
            }
        } else {
            // redis 不存在 "task_all_list_" + taskGroup + "_" + execDate 进行初始化
            isInit = true;
        }

        if (isInit) {
            initTaskExecForRedis(batchSysCode, execDate, taskGroup);
        }
    }


    public boolean checkTaskExecEnd(String batchSysCode, String taskGroup, String execDate, String snatchingMode) throws TaskExecException {
        if ("".equals(batchSysCode) || "".equals(execDate) || "".equals(taskGroup) || "".equals(snatchingMode))
            throw new TaskExecException("输入参数异常，batchSysCode、execDate、taskGroup、snatchingMode 不能为空！", TaskStatusCodeEnum.TASK_GETTASK_ERROR, "");
        // 验证当前 是否 存在执行错误的
        if (snatchingMode.equals("redis")) {
            // 获取 redis 列表
            String lockKey = getGlobalLockKey(batchSysCode, taskGroup, execDate);
            String listKey = getTaskListKey(batchSysCode, taskGroup, execDate);
            if (getRedisLock(lockKey, "getClearBasicTaskExecByDatabaseByRedis" + "_" + System.currentTimeMillis())) {
                try {
                    // 获取 分布式锁成功
                    // 遍历 redis 数据结果
                    List<BatchClearBasicTaskExec> taskList = getRange(listKey);
                    return checkTaskExecEnd(taskList);

                } finally {
                    unRedisLock(lockKey);
                }

            }
        }

        if (snatchingMode.equals("database")) {
            List<BatchClearBasicTaskExec> taskList = clearBasicTaskExecService.list(new LambdaQueryWrapper<BatchClearBasicTaskExec>()
                    .eq(BatchClearBasicTaskExec::getBatchSysCode, batchSysCode)
                    .eq(BatchClearBasicTaskExec::getTaskGroup, taskGroup)
                    .eq(BatchClearBasicTaskExec::getExecDate, execDate)
            );
            return checkTaskExecEnd(taskList);
        }
        return false;
    }

    public boolean checkTaskDailyExecEnd(String batchSysCode, String execDate) throws TaskDailyException {
        if ("".equals(batchSysCode) || "".equals(execDate))
            throw new TaskDailyException("输入参数异常，batchSysCode、execDate、 不能为空！", TaskDailyStatusCodeEnum.TASK_GETTASK_ERROR, "");

        String currentHHmmss = CommonUtil.getHHmmss(new Date()).substring(0, 2) + "0000";
        long count = dailyBasicTaskStepExecService.count(new LambdaQueryWrapper<BatchDailyBasicTaskStepExec>()
                .eq(BatchDailyBasicTaskStepExec::getBatchSysCode, batchSysCode)
                .eq(BatchDailyBasicTaskStepExec::getExecDate, execDate)
                .and(i -> i.eq(BatchDailyBasicTaskStepExec::getExecStatus, "0").le(BatchDailyBasicTaskStepExec::getExecTime, currentHHmmss).or()
                        .and(j -> j.eq(BatchDailyBasicTaskStepExec::getExecStatus, "4").eq(BatchDailyBasicTaskStepExec::getIsReplay, "1")))
        );

        return count == 0;
    }

    private List<BatchClearBasicTaskExec> getRange(String listKey) {
        List range = redisTemplate.opsForList().range(listKey, 0, -1);
        if (range == null) {
            range = new ArrayList<BatchClearBasicTaskExec>();
        }
        return range;
    }


    private List<String> getRangeReturnStr(String listKey) {
        List range = redisTemplate.opsForList().range(listKey, 0, -1);
        if (range == null) {
            range = new ArrayList<String>();
        }
        return range;
    }

    private boolean checkTaskExecEnd(List<BatchClearBasicTaskExec> taskList) {
        // 不存在列表
        if (taskList.isEmpty())
            return true;
        // 不存在待执行列表 不存在 exec_status = ‘0’ 或者 exec_status = ‘1’ 未执行的列表
        if (taskList.stream().noneMatch(item -> item.getExecStatus().equals("0") || item.getExecStatus().equals("1"))) {
            return true;
        }
        // 存在执行失败的 exec_status = ‘4’
        return taskList.stream().anyMatch(item -> item.getExecStatus().equals("4"));
    }


    public List<BatchDailyBasicTaskStepExec> getTaskDailyExec(String batchSysCode, String execDate, int maxNum) throws TaskDailyException {
        long start = System.currentTimeMillis();

        String currentHHmmss = CommonUtil.getHHmmss(new Date()).substring(0, 2) + "0000";

        // List<BatchDailyBasicTaskStepExec> returnList = dailyBasicTaskStepExecService.list(new LambdaQueryWrapper<BatchDailyBasicTaskStepExec>()
        //         .eq(BatchDailyBasicTaskStepExec::getBatchSysCode, batchSysCode)
        //         .eq(BatchDailyBasicTaskStepExec::getExecDate, execDate)
        //         .le(BatchDailyBasicTaskStepExec::getExecTime, currentHHmmss)
        //         .apply("(exec_status = {0} or (exec_status = {1} and is_replay = {2}))", "0", "4", "1")
        //
        // );
        List<BatchDailyBasicTaskStepExec> returnList = dailyBasicTaskStepExecService.getTask(batchSysCode, execDate, currentHHmmss);

        // 限制 返回 数量
        if (returnList.size() > maxNum) {
            returnList = returnList.subList(0, maxNum);
        }


        // getClearBasicTaskExecByRedis

        writeBackTaskDailyExecForRedis(returnList, batchSysCode, execDate);
        long end = System.currentTimeMillis();

        log.info("getTaskDailyExec 抢夺耗时：" + (end - start));
        return returnList;

    }

    /**
     * 回写 日间跑批任务 写入到 redis
     */
    private void writeBackTaskDailyExecForRedis(List<BatchDailyBasicTaskStepExec> taskDailyExecList, String batchSysCode, String execDate) throws TaskDailyException {
        initREDIS_VALUE_EXPIRE(false);

        String lockKey = getDailyGlobalLockKey(batchSysCode, execDate);
        if (getRedisLock(lockKey, "writeBackTaskDailyExecForRedis" + "_" + System.currentTimeMillis())) {
            try {
                //  此方法用于监测 已占用任务 长时间未执行 内容 进行状态回收
                taskDailyExecRunningOverTimeGC(batchSysCode, execDate);

                if (taskDailyExecList != null) {
                    // 抢夺成功 更新 redis 的 task_all_list_[taskGroup]_[execDate]

                    // 记录 以抢夺 任务时间，以便监控任务执行情况，针对超时未执行的任务进行回收 task_get_[taskGroup]_[execDate]_[taskExecId]
                    for (BatchDailyBasicTaskStepExec item : taskDailyExecList) {
                        redisTemplate.opsForValue().set(getTaskDailyLockKey(item.getBatchSysCode(),
                                        item.getExecDate(), item.getTaskExecid()), System.currentTimeMillis(),
                                REDIS_VALUE_EXPIRE,
                                REDIS_VALUE_EXPIRE_TIMEUNIT);
                        item.setExecStatus("1");
                        dailyBasicTaskStepExecService.updateByObject(item);

                    }
                }
            } finally {
                unRedisLock(lockKey);
            }

        } else {
            throw new TaskDailyException("抢夺失败未获取可执行步骤！", TaskDailyStatusCodeEnum.TASK_GETTASK_NULL_SUCCESS, "");
        }

    }

    /**
     * 此方法用于监测 已占用任务 长时间未执行 内容 进行状态回收 将 execStatus 1 -> 0 清除 redis缓存
     */
    private void taskDailyExecRunningOverTimeGC(String batchSysCode, String execDate) throws TaskDailyException {
        log.info("taskDailyExecRunningOverTimeGC  start");
        initREDIS_VALUE_EXPIRE(false);
        initREDIS_TASK_RUNNING_MAX_OVERTIME(false);

        String lastHHmmss = CommonUtil.getHHmmssByDiffDateHour(new Date(), -1);

        // 回收 跑批日间任务执行表
        List<BatchDailyBasicTaskStepExec> waitGCList = dailyBasicTaskStepExecService.list(
                new LambdaQueryWrapper<BatchDailyBasicTaskStepExec>()
                        .eq(BatchDailyBasicTaskStepExec::getBatchSysCode, batchSysCode)
                        .eq(BatchDailyBasicTaskStepExec::getExecDate, execDate)
                        .eq(BatchDailyBasicTaskStepExec::getExecStatus, "1")
                        .le(BatchDailyBasicTaskStepExec::getExecTime, lastHHmmss)
        );

        long end = System.currentTimeMillis();
        for (BatchDailyBasicTaskStepExec item : waitGCList) {
            // 验证 缓存 是否 存在 任务占用记录
            Long taskRunningStartTime = (Long) redisTemplate.opsForValue().get(getTaskDailyLockKey(item.getBatchSysCode(),
                    item.getExecDate(), item.getTaskExecid()));
            if (taskRunningStartTime == null) {
                // task_get_[taskGroup]_[execDate]_[taskExecId]
                log.info("taskDailyExecRunningOverTimeGC  taskExecid[" + item.getTaskExecid() + "] 状态已分配 redis 分配锁未获取，重置分配锁 key[" +
                        getTaskDailyLockKey(item.getBatchSysCode(), item.getExecDate(), item.getTaskExecid()) + "]");

                redisTemplate.opsForValue().set(getTaskDailyLockKey(item.getBatchSysCode(), item.getExecDate(),
                        item.getTaskExecid()), end, REDIS_VALUE_EXPIRE, REDIS_VALUE_EXPIRE_TIMEUNIT);
                continue;
            }

            long start = taskRunningStartTime;

            // 长时间 占用 未处理
            // 再次确认此taskExec 状态为 待执行
            if ((end - start) > REDIS_TASK_RUNNING_MAX_OVERTIME && dailyBasicTaskStepExecService.count(
                    new LambdaQueryWrapper<BatchDailyBasicTaskStepExec>().eq(BatchDailyBasicTaskStepExec::getTaskExecid, item.getTaskExecid()).eq(BatchDailyBasicTaskStepExec::getExecStatus, "1")) > 0) {
                log.info("taskExecRunningOverTimeGC  taskExecid[" + item.getTaskExecid() + "] 状态已分配 跑批任务超过[" + REDIS_TASK_RUNNING_MAX_OVERTIME +
                        "]未执行，回收 taskExecid[" + item.getTaskExecid() + "] redis key[" + getTaskDailyLockKey(item.getBatchSysCode(),
                        item.getExecDate(), item.getTaskExecid()) + "] delete！");

                // 进行状态回收
                // 更新 数据库状态
                item.setExecStatus("0");
                String errMsg;
                try {
                    dailyBasicTaskStepExecService.updateByObject(item);
                } catch (Exception e) {
                    errMsg = "batchClearBasicTaskExecService updateByObject:" + item.getTaskExecid();

                    log.error(errMsg);
                    throw new TaskDailyException(errMsg, TaskDailyStatusCodeEnum.TASK_RUNNING_RUNNINGERROR, item.getTaskExecid());

                }

            }
        }

    }


    /**
     * 获取可执行清算任务
     *
     * @param batchSysCode 跑批系统代码
     * @param taskGroup    清算组代码
     * @param execDate     执行日期
     * @param taskExecid   批量任务执行ID
     * @return 清算任务对象
     * @throws TaskExecException 自定义清算任务异常对象
     */
    public BatchClearBasicTaskExec getTaskExec(String batchSysCode, String taskGroup, String execDate, String taskExecid, String snatchingMode) throws TaskExecException {
        long start = System.currentTimeMillis();
        if ("".equals(batchSysCode) || "".equals(execDate) || "".equals(taskGroup))
            throw new TaskExecException("输入参数异常，batchSysCode、execDate、taskGroup 不能为空！", TaskStatusCodeEnum.TASK_GETTASK_ERROR, "");

        // 验证当前是否 可进行 抢夺任务
        BatchClearBasicTaskExec taskExec;
        switch (snatchingMode) {
            case "database":
                taskExec = getClearBasicTaskExecByDatabase(batchSysCode, taskGroup, execDate, taskExecid);
                break;
            case "redis":
                taskExec = getClearBasicTaskExecByRedis(batchSysCode, taskGroup, execDate, taskExecid);
                break;
            default:
                taskExec = null;
                break;
        }

        long end = System.currentTimeMillis();

        log.info("taskExec 抢夺耗时：" + (end - start));
        return taskExec;

    }

    /**
     * 判断当前 是否为抢夺时间 通过读取 redis 缓存 未获取标志位 抛出异常
     * todo-wuwei 需要优化 调整返回结果为boolean或者 request
     *
     * @param request request.getTaskGroup  和 request.getExecDate 不能为空
     */
    public void checkIsTaskRunningTime(BatchExecReq request) throws TaskExecException {
        String taskGroup = request.getTaskGroup();
        String execDate = request.getExecDate();
        String batchSysCode = request.getBatchSysCode();
        if (!(StringUtils.hasLength(request.getBatchSysCode()) && StringUtils.hasLength(request.getTaskGroup()) && StringUtils.hasLength(request.getExecDate()))) {
            log.info("request 请求参数异常！batchSysCode or taskGroup or snatchingMode or taskDate is null");
            return;
        }
        // 不存在 抢夺任务 启动标志
        if (Boolean.FALSE.equals(redisTemplate.hasKey(getTaskRunningKey(batchSysCode, taskGroup, execDate)))) {
            throw new TaskExecException("抢夺任务失败！跑批任务未开启！", TaskStatusCodeEnum.TASK_ERROR, "");
        }
    }

    /**
     * @param request request.getBatchSysCode  和 request.getExecDate 不能为空
     */
    public boolean checkIsTaskDailyRunningTime(BatchDailyExecReq request) {
        String execDate = request.getExecDate();
        String batchSysCode = request.getBatchSysCode();
        if (!(StringUtils.hasLength(batchSysCode) && StringUtils.hasLength(execDate))) {
            log.info("request 请求参数异常！batchSysCode or taskDate is null");
            return false;
        }
        return true;
    }


    /**
     * 获取跑批状态
     */
    public Boolean getTaskRunningStatus(BatchExecReq request) {
        String taskGroup = request.getTaskGroup();
        String execDate = request.getExecDate();
        String batchSysCode = request.getBatchSysCode();
        if (!(StringUtils.hasLength(request.getBatchSysCode()) && StringUtils.hasLength(request.getTaskGroup()) && StringUtils.hasLength(request.getExecDate()))) {
            log.info("request 请求参数异常！batchSysCode or taskGroup or snatchingMode or taskDate is null");
            return false;
        }
        return redisTemplate.hasKey(getTaskRunningKey(batchSysCode, taskGroup, execDate));
    }


    /**
     * 强制停止 redis 缓存 未获取标志 抛出异常
     */
    public void forceToStopTaskRunningTime(BatchExecReq request) throws TaskExecException {
        String batchSysCode = request.getBatchSysCode();
        String taskGroup = request.getTaskGroup();
        String execDate = request.getExecDate();
        String snatchingMode = request.getSnatchingMode();

        if (!(StringUtils.hasLength(request.getBatchSysCode()) && StringUtils.hasLength(request.getTaskGroup()) && StringUtils.hasLength(request.getExecDate()) && StringUtils.hasLength(request.getSnatchingMode()))) {
            log.info("request 请求参数异常！batchSysCode or taskGroup or snatchingMode or taskDate is null");
            throw new TaskExecException("request 请求参数异常！batchSysCode or taskGroup or snatchingMode or taskDate is null",
                    TaskStatusCodeEnum.TASK_ERROR, "");
        }

        // 存在 抢夺任务 启动标志
        if (Boolean.TRUE.equals(redisTemplate.hasKey(getTaskRunningKey(batchSysCode, taskGroup, execDate)))) {
            // 删除 全局锁
            if (!delRedis(getGlobalLockKey(batchSysCode, taskGroup, execDate), getTaskRunningKey(batchSysCode, taskGroup, execDate))) {
                throw new TaskExecException("删除跑批任务开启标志失败!redis[" + getTaskRunningKey(batchSysCode, taskGroup, execDate) + "]",
                        TaskStatusCodeEnum.TASK_ERROR, "");
            }

        }

        // 验证当前是否存在 在执行的任务
        LambdaQueryWrapper<BatchClearBasicTaskExec> query_where = new LambdaQueryWrapper<BatchClearBasicTaskExec>()
                .eq(BatchClearBasicTaskExec::getBatchSysCode, batchSysCode)
                .eq(BatchClearBasicTaskExec::getTaskGroup, taskGroup)
                .eq(BatchClearBasicTaskExec::getExecDate, execDate)
                .in(BatchClearBasicTaskExec::getExecStatus, "2")// 执行状态 (0-未执行; 1-占用中; 2-执行中;   3-执行成功; 4-执行失败; 5-跳过执行; 6-任务终止; )
                ;
        // 查询当前是否 存在正在执行的任务
        if (clearBasicTaskExecService.count(query_where) > 0) {
            throw new TaskExecException("删除跑批任务开启标志成功!但当前存在执行中的任务！请稍后再试", TaskStatusCodeEnum.TASK_ERROR, "");

        }

        // 验证当前重置模式 执行模式 针对 redis 清除相关列表
        if ("redis".equals(snatchingMode)) {
            if (Boolean.TRUE.equals(redisTemplate.hasKey(getTaskListKey(batchSysCode, taskGroup, execDate)))) {

                List<BatchClearBasicTaskExec> taskExecList = getRange(getTaskListKey(batchSysCode, taskGroup, execDate));

                // 删除 全局锁
                if (!delRedis(getGlobalLockKey(batchSysCode, taskGroup, execDate), getTaskListKey(batchSysCode, taskGroup, execDate), "list")) {
                    throw new TaskExecException("删除跑批任务redis列表失败!redis[" + getTaskListKey(batchSysCode, taskGroup, execDate) + "]",
                            TaskStatusCodeEnum.TASK_ERROR, "");
                }
                // 删除 抢夺临时锁
                for (BatchClearBasicTaskExec item : taskExecList) {
                    if (Boolean.TRUE.equals(redisTemplate.hasKey(getTaskLockKey(batchSysCode, taskGroup, execDate, item.getTaskExecid())))) {
                        if (!delRedis(getGlobalLockKey(batchSysCode, taskGroup, execDate), getTaskLockKey(batchSysCode, taskGroup, execDate,
                                item.getTaskExecid()), "key")) {
                            throw new TaskExecException("删除跑批任务临时redis锁!redis[" + getTaskLockKey(batchSysCode, taskGroup, execDate,
                                    item.getTaskExecid()) + "]", TaskStatusCodeEnum.TASK_ERROR, "");
                        }
                    }
                }


            }
        }


    }


    public void checkThreadPool() throws TaskExecException {
        int idleCount = BatchThreadPool.getIdleThreadCount();

        log.info("可执行线程数:[{}]", idleCount);
        if (idleCount <= 0) {
            // 休眠5秒钟，如果5秒后线程池仍满，则退出任务调度
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                log.info(e.getMessage());
                // e.printStackTrace();
            }
            if (BatchThreadPool.getIdleThreadCount() <= 0) {
                throw new TaskExecException("批量任务线程池已满,退出本次批量任务调度", TaskStatusCodeEnum.TASK_RUNNING_ERROR, "");
            }
        }
    }

    public int getThreadPoolRuningCount() throws TaskExecException {
        return BatchThreadPool.getActiveCount();
    }

    public boolean insertTaskWaittingList(String batchSysCode, String taskGroup, String execDate, String snatchingMode) {
        initREDIS_VALUE_EXPIRE(false);
        // 判断 是否 存在 TaskWaittingList
        String taskWaittingValue = batchSysCode + "_" + taskGroup + "_" + execDate + "_" + snatchingMode;
        boolean result = false;
        if (getRedisLock(getTaskWaittingKey(batchSysCode) + "_lock", "initTaskExecForRedis" + "_" + System.currentTimeMillis())) {
            try {
                // 增加去重操作
                // if (redisTemplate.opsForList().indexOf(getTaskWaittingKey(), taskWaittingValue) == null) {
                if (!redisTemplateListIndexOf(redisTemplate.opsForList(), getTaskWaittingKey(batchSysCode), taskWaittingValue)) {
                    redisTemplate.opsForList().rightPush(getTaskWaittingKey(batchSysCode), taskWaittingValue);
                    redisTemplate.boundValueOps(getTaskWaittingKey(batchSysCode)).expire(REDIS_VALUE_EXPIRE, REDIS_VALUE_EXPIRE_TIMEUNIT);
                    log.info("insRedis method finish@lockKey->" + getTaskWaittingKey(batchSysCode) + "_lock" + ";key->" + getTaskWaittingKey(batchSysCode) + "value->" + taskWaittingValue);
                }
                result = true;
            } finally {
                unRedisLock(getTaskWaittingKey(batchSysCode) + "_lock");
            }
        }

        CommonUtil.showLogInfo(getRange(getTaskWaittingKey(batchSysCode)), "insertTaskWaittingList currentTaskWaittingList");


        return result;
    }


    private boolean redisTemplateListIndexOf(ListOperations<String, String> listOperations, String key, String value) {
        if (listOperations.size(key) == 0) {
            return false;
        }
        boolean result = false;
        for (String s : Objects.requireNonNull(listOperations.range(key, 0, -1))) {
            if (s.equals(value)) {
                result = true;
                break;
            }
        }

        return result;
    }


    /**
     * 获取待执行列表
     * 针对 同一taskGroup 下 存在 多个多执行业务期 系统 自动 返回日期最靠前的信息
     * todo-wuwei 待优化 针对异常请求 增加处理
     */
    public String getTaskWaittingValue(BatchExecReq request) {
        String batchSysCode = request.getBatchSysCode();
        String taskGroup = request.getTaskGroup();
        String exec_date = request.getExecDate();
        if (!(StringUtils.hasLength(request.getBatchSysCode()) && StringUtils.hasLength(request.getTaskGroup()) && StringUtils.hasLength(request.getExecDate()))) {
            log.info("request 请求参数异常！batchSysCode or taskGroup or snatchingMode or taskDate is null");
            return "";
        }
        if (Boolean.FALSE.equals(redisTemplate.hasKey(getTaskWaittingKey(batchSysCode))) || redisTemplate.opsForList().size(getTaskWaittingKey(batchSysCode)) == 0) {
            return "";
        }
        List<String> taskWaittingList = getRangeReturnStr(getTaskWaittingKey(batchSysCode));

        if (taskWaittingList.isEmpty()) {
            return "";
        }
        taskWaittingList = taskWaittingList.stream().sorted().collect(Collectors.toList());

        Object value = null;

        for (Object o : taskWaittingList) {
            String[] keyList = o.toString().split("_");
            if (keyList.length < 4) {
                continue;
            }
            String temp_batchSysCode = keyList[0];
            String temp_taskGroup = keyList[1];
            String temp_execDate = keyList[2];

            if (batchSysCode.equals(temp_batchSysCode) && taskGroup.equals(temp_taskGroup) && ((new BigDecimal(exec_date).compareTo(new BigDecimal(temp_execDate)) >= 0))) {
                value = o;
                break;
            }
        }
        return value == null ? "" : value.toString();
    }

    /**
     * 验证 当前跑批组 是否 存在 已初始化 未执行跑批组
     *
     * @param taskGroup 跑批组id
     * @param execDate  跑批执行时间
     * @return false -> 扫描数据库无待执行跑批组 true -> 扫描数据库存在待执行跑批组
     */
    public boolean checkTaskWaittingValue(String batchSysCode, String taskGroup, String execDate) {

        if ((StringUtils.hasLength(batchSysCode) && StringUtils.hasLength(taskGroup) && StringUtils.hasLength(execDate))) {
            log.info("request 请求参数异常！batchSysCode or taskGroup or snatchingMode or taskDate is null");
            return false;
        }
        // 获取 当前 跑批组 跑批执行时间 已产生
        long unRunningCount =
                clearBasicTaskExecService.count(new LambdaQueryWrapper<BatchClearBasicTaskExec>().eq(BatchClearBasicTaskExec::getBatchSysCode,
                        batchSysCode).eq(BatchClearBasicTaskExec::getTaskGroup, taskGroup).eq(BatchClearBasicTaskExec::getExecDate, execDate).eq(BatchClearBasicTaskExec::getExecStatus, "0"));
        long allRunningCount =
                clearBasicTaskExecService.count(new LambdaQueryWrapper<BatchClearBasicTaskExec>().eq(BatchClearBasicTaskExec::getBatchSysCode,
                        batchSysCode).eq(BatchClearBasicTaskExec::getTaskGroup, taskGroup).eq(BatchClearBasicTaskExec::getExecDate, execDate));
        boolean checkResult = unRunningCount == allRunningCount && unRunningCount != 0;
        if (checkResult) {
            BatchExecReq request = new BatchExecReq();
            request.setTaskGroup(XssFilterUtils.filterXss(taskGroup));
            request.setExecDate(XssFilterUtils.filterXss(execDate));
            request.setBatchSysCode(XssFilterUtils.filterXss(batchSysCode));
            request.setSnatchingMode(XssFilterUtils.filterXss("redis"));
            String errMsg;
            // 数据库扫描 确认 存在整组未执行 跑批列表`
            try {
                checkIsTaskRunningTime(request);
                errMsg = "跑批平台-重置跑批任务执行存在redis执行全局锁";
                log.error(errMsg);
                return false;
            } catch (TaskExecException e) {
                // log.info("taskExecFactory.checkIsTaskRunningTime 运行异常！" + e.getMessage());
            }

            try {
                // errMsg = "重置跑批任务失败";
                // 验证 execDate 有效性
                checkExecDate(request.getExecDate());
                // 验证 当前 是否 存在运行中的线程
                if (getThreadPoolRuningCount() > 0) {
                    errMsg = "当前跑批线程池存在执行中的任务" + getThreadPoolRuningCount();
                    log.error(errMsg);
                    // 当前存在执行的线程 不允许开启 跑批任务
                    return false;

                }
                forceToStopTaskRunningTime(request);

                if (resetTask(request)) {
                    checkResult = true;
                    // return Result.ok("重置跑批任务成功");
                }

                // return Result.error("重置跑批任务失败");

            } catch (TaskExecException e) {
                errMsg = e.getMessage();
                log.info(errMsg);
                checkResult = false;

                // return Result.error(e.getMessage());

            } catch (Exception e) {
                log.error("重置跑批任务：");
                log.error(e.getMessage(), e);
                errMsg = e.getMessage();
                log.info(errMsg);
                checkResult = false;
                // return Result.error(e.getMessage());
            }
        }

        return checkResult;
    }

    public boolean confirmTaskWaittingValue(BatchExecReq request) {
        if (!(StringUtils.hasLength(request.getBatchSysCode()) && StringUtils.hasLength(request.getTaskGroup()) && StringUtils.hasLength(request.getExecDate()))) {
            log.info("request 请求参数异常！batchSysCode or taskGroup or snatchingMode or taskDate is null");
            return false;
        }

        if (Boolean.FALSE.equals(redisTemplate.hasKey(getTaskWaittingKey(request.getBatchSysCode()))) || redisTemplate.opsForList().size(getTaskWaittingKey(request.getBatchSysCode())) == 0) {
            return true;
        }
        boolean result = false;

        String value = request.getBatchSysCode() + "_" + request.getTaskGroup() + "_" + request.getExecDate() + "_" + request.getSnatchingMode();
        if (getRedisLock(getTaskWaittingKey(request.getBatchSysCode()) + "_lock", "initTaskExecForRedis" + "_" + System.currentTimeMillis())) {
            try {
                Long remove = redisTemplate.opsForList().remove(getTaskWaittingKey(request.getBatchSysCode()), 1, value);
                result = remove != null && remove != 0;
                log.info("confirmTaskWaittingValue method finish@lockKey->" + getTaskWaittingKey(request.getBatchSysCode()) + "_lock" + ";key->" + getTaskWaittingKey(request.getBatchSysCode()) + ";value->" + value + ";result->" + result);

            } finally {
                unRedisLock(getTaskWaittingKey(request.getBatchSysCode()) + "_lock");
            }
        }

        CommonUtil.showLogInfo(getRange(getTaskWaittingKey(request.getBatchSysCode())), "confirmTaskWaittingValue currentTaskWaittingList");

        return result;

    }


    /**
     * 针对 redis进行操作 操作成功后进行落库操作
     *
     * @param taskGroup  跑批组id
     * @param execDate   跑批执行日期
     * @param taskExecid 跑批执行id
     * @return 跑批对象
     */
    private BatchClearBasicTaskExec getClearBasicTaskExecByRedis(String batchSysCode, String taskGroup, String execDate, String taskExecid) throws TaskExecException {
        initREDIS_VALUE_EXPIRE(false);
        if ((StringUtils.hasLength(batchSysCode) && StringUtils.hasLength(taskGroup) && StringUtils.hasLength(execDate) && StringUtils.hasLength(taskExecid))) {
            log.info("request 请求参数异常！batchSysCode or taskGroup or snatchingMode or execDate or taskExecid is null");
            throw new TaskExecException("request 请求参数异常！batchSysCode or taskGroup or snatchingMode or execDate or taskExecid is null",
                    TaskStatusCodeEnum.TASK_INIT_ERROR, "");

        }
        BatchClearBasicTaskExec clearBasicTaskExec;
        String lockKey = getGlobalLockKey(batchSysCode, taskGroup, execDate);
        String listKey = getTaskListKey(batchSysCode, taskGroup, execDate);
        if (getRedisLock(lockKey, "getClearBasicTaskExecByDatabaseByRedis" + "_" + System.currentTimeMillis())) {
            try {
                // 获取 分布式锁成功
                // 遍历 redis 数据结果
                List<BatchClearBasicTaskExec> taskList = getRange(listKey);

                if (taskList.isEmpty())
                    throw new TaskExecException("redis模式抢夺批量任务异常！ redis key[" + getTaskListKey(batchSysCode, taskGroup, execDate) +
                            "]不存在有效值。请确认抢夺任务初始化是否成功或者抢夺任务初始化模式是否为redis模式", TaskStatusCodeEnum.TASK_GETTASK_ERROR, "");

                taskList = taskExecRunningOverTimeGC(taskList, "redis");
                // 重新初始化 redis 待执行列表 针对抢夺成功但未执行的内容
                List<BatchClearBasicTaskExec> availableTaskExecList;
                if ("".equals(taskExecid)) {
                    availableTaskExecList =
                            taskList.stream().filter(item -> "0".equals(item.getExecStatus()) || "1".equals(item.getExecStatus()) || "2".equals(item.getExecStatus()) || "4".equals(item.getExecStatus())).collect(Collectors.toList());
                } else {
                    availableTaskExecList =
                            taskList.stream().filter(item -> (("0".equals(item.getExecStatus()) || "1".equals(item.getExecStatus()) || "2".equals(item.getExecStatus()) || "4".equals(item.getExecStatus())) && (item.getTaskExecid().equals(taskExecid)))).collect(Collectors.toList());
                }
                availableTaskExecList = getAvailableTaskExec(availableTaskExecList);
                clearBasicTaskExec = getClearBasicTaskExecByAvailableTaskExec(availableTaskExecList);


                if (clearBasicTaskExec != null) {
                    // 抢夺成功 更新 redis 的 task_all_list_[taskGroup]_[execDate]
                    updateRedisListValue(clearBasicTaskExec, listKey, taskList);
                    // 记录 以抢夺 任务时间，以便监控任务执行情况，针对超时未执行的任务进行回收 task_get_[taskGroup]_[execDate]_[taskExecId]

                    redisTemplate.opsForValue().set(getTaskLockKey(clearBasicTaskExec.getBatchSysCode(), clearBasicTaskExec.getTaskGroup(),
                                    clearBasicTaskExec.getExecDate(), clearBasicTaskExec.getTaskExecid()), System.currentTimeMillis(),
                            REDIS_VALUE_EXPIRE,
                            REDIS_VALUE_EXPIRE_TIMEUNIT);

                }
                // 删除 分布式锁

            } finally {
                unRedisLock(lockKey);


            }

        } else {
            throw new TaskExecException("抢夺失败未获取可执行步骤！", TaskStatusCodeEnum.TASK_GETTASK_NULL_SUCCESS, "");
        }

        return clearBasicTaskExec;


    }


    /**
     * 此方法用于监测 已占用任务 长时间未执行 内容 进行状态回收 将 execStatus 1 -> 0 清除 redis缓存
     *
     * @param taskList 跑批对象列表
     * @param gcMode   回收机制 redis回收 -> 更新数据库、更新redis、删除已占用的缓存  database 数据库回收 -> 更新数据库、删除已占用的缓存
     */
    private List<BatchClearBasicTaskExec> taskExecRunningOverTimeGC(List<BatchClearBasicTaskExec> taskList, String gcMode) throws TaskExecException {
        log.info("taskExecRunningOverTimeGC  start");
        initREDIS_VALUE_EXPIRE(false);
        initREDIS_TASK_RUNNING_MAX_OVERTIME(false);

        List<BatchClearBasicTaskExec> gcTaskList = taskList.stream().filter(item -> item.getExecStatus().equals("1")).collect(Collectors.toList());
        if (gcTaskList.isEmpty()) {
            log.info("taskExecRunningOverTimeGC  unUse");
            return taskList;
        }
        long end = System.currentTimeMillis();
        for (BatchClearBasicTaskExec item : gcTaskList) {
            // 验证 缓存 是否 存在 任务占用记录
            Long taskRunningStartTime = (Long) redisTemplate.opsForValue().get(getTaskLockKey(item.getBatchSysCode(), item.getTaskGroup(),
                    item.getExecDate(), item.getTaskExecid()));
            if (taskRunningStartTime == null) {
                // task_get_[taskGroup]_[execDate]_[taskExecId]
                log.info("taskExecRunningOverTimeGC  taskExecid[" + item.getTaskExecid() + "] 状态已分配 redis 分配锁未获取，重置分配锁 key[" + getTaskLockKey(item.getBatchSysCode(), item.getTaskGroup(), item.getExecDate(), item.getTaskExecid()) + "]");

                redisTemplate.opsForValue().set(getTaskLockKey(item.getBatchSysCode(), item.getTaskGroup(), item.getExecDate(),
                        item.getTaskExecid()), end, REDIS_VALUE_EXPIRE, REDIS_VALUE_EXPIRE_TIMEUNIT);
                continue;
            }

            long start = taskRunningStartTime;

            // 长时间 占用 未处理
            // 再次确认此taskExec 状态为 待执行
            if ((end - start) > REDIS_TASK_RUNNING_MAX_OVERTIME && clearBasicTaskExecService.count(new LambdaQueryWrapper<BatchClearBasicTaskExec>().eq(BatchClearBasicTaskExec::getTaskExecid, item.getTaskExecid()).eq(BatchClearBasicTaskExec::getExecStatus, "1")) > 0) {
                log.info("taskExecRunningOverTimeGC  taskExecid[" + item.getTaskExecid() + "] 状态已分配 跑批任务超过[" + REDIS_TASK_RUNNING_MAX_OVERTIME +
                        "]未执行，回收 taskExecid[" + item.getTaskExecid() + "] redis key[" + getTaskLockKey(item.getBatchSysCode(), item.getTaskGroup(),
                        item.getExecDate(), item.getTaskExecid()) + "] delete！");

                // 进行状态回收
                // 更新 数据库状态
                item.setExecStatus("0");
                String errMsg;
                try {
                    clearBasicTaskExecService.updateByObject(item);
                } catch (Exception e) {
                    errMsg = "batchClearBasicTaskExecService updateByObject:" + item.getTaskExecid();

                    log.error(errMsg);
                    throw new TaskExecException(errMsg, TaskStatusCodeEnum.TASK_RUNNING_RUNNINGERROR, item.getTaskExecid());

                }

                // 更新 redis list数据
                //  String listKey = "task_all_list_"+taskGroup+"_"+ execDate;
                if (gcMode.equals("redis")) {
                    log.info("taskExecRunningOverTimeGC  taskExecid[" + item.getTaskExecid() + "] 状态已分配 跑批任务超过[" + REDIS_TASK_RUNNING_MAX_OVERTIME + "]未执行，回收 taskExecid[" + item.getTaskExecid() + "],回滚 redis key[" + getTaskListKey(item.getBatchSysCode(), item.getTaskGroup(), item.getExecDate()) + "] execStatus！");
                    updateRedisListValue(item);
                }
                for (BatchClearBasicTaskExec batchClearBasicTaskExec : taskList) {
                    if (Objects.equals(batchClearBasicTaskExec.getId(), item.getId())) {
                        batchClearBasicTaskExec.setExecStatus("0");
                        break;
                    }
                }
                log.info("taskExecRunningOverTimeGC  taskExecid[" + item.getTaskExecid() + "] 状态已分配 跑批任务超过[" + REDIS_TASK_RUNNING_MAX_OVERTIME +
                        "]未执行，删除 redis key[" + getTaskLockKey(item.getBatchSysCode(), item.getTaskGroup(), item.getExecDate(),
                        item.getTaskExecid()) + "]");

                delRedis(getGlobalLockKey(item.getBatchSysCode(), item.getTaskGroup(), item.getExecDate()), getTaskLockKey(item.getBatchSysCode(),
                        item.getTaskGroup(), item.getExecDate(), item.getTaskExecid()));
                // redisTemplate.delete("task_get_" + item.getTaskGroup() + "_" + item.getExecDate() + "_" + item.getTaskExecid());
            }
        }


        return taskList;
    }

    /**
     * 获取 redis 分布式锁 主要是 针对 对 task_all_list_[taskGroup]_[execDate] 进行操作前 进行全局加锁
     */
    private boolean getRedisLock(String key, String value) {
        initREDIS_GET_LOCK_MAX_NUM(false);
        initREDIS_TASK_LIST_EXPIRE(false);
        // 设置 分布式锁
        int getLockNum = 0;
        // boolean result = false;
        // 创建 分布式锁 默认锁定2分钟
        while (!Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, value, REDIS_TASK_LIST_EXPIRE, REDIS_TASK_LIST_EXPIRE_TIMEUNIT))) {
            if (getLockNum == REDIS_GET_LOCK_MAX_NUM) {
                break;
            }

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                log.info(e.getMessage());
                getLockNum = REDIS_GET_LOCK_MAX_NUM;
                break;
            }
            log.info("[ getRedisLock redis]设置锁缓存失败！ 重复{}次,最大执行{}次！", getLockNum, REDIS_GET_LOCK_MAX_NUM);
            getLockNum++;

        }

        log.info("[redisTemplate redis]设置锁缓存{} key:{},value:{}", (getLockNum < REDIS_GET_LOCK_MAX_NUM) ? "成功" : "失败", key, value);
        return getLockNum < REDIS_GET_LOCK_MAX_NUM;
    }

    private void unRedisLock(String key) {
        final boolean result = Boolean.TRUE.equals(redisTemplate.delete(key));
        log.info("[redisTemplate redis]释放锁缓存{} key:{}", result ? "成功" : "失败", key);
        // return result;
    }

    private boolean insRedis(String lockKey, String key, Object value, int timeout, TimeUnit timeUnit) {
        if (getRedisLock(lockKey, "initTaskExecForRedis" + "_" + System.currentTimeMillis())) {
            try {

                insRedis(key, value, timeout, timeUnit);
                log.info("insRedis method finish@lockKey->" + lockKey + ";key->" + key);

            } finally {
                unRedisLock(lockKey);
            }
        }

        return true;
    }

    private void insRedis(String key, Object value, int timeout, TimeUnit timeUnit) {
        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
    }

    /**
     * 删除 redis(增加了 redis 分布式锁)
     *
     * @param lockKey 分布式锁 key
     * @param key     删除key
     * @return 删除结果
     */
    private boolean delRedis(String lockKey, String key) {

        return delRedis(lockKey, key, "key");
    }

    private boolean delRedis(String lockKey, String key, String redisDataType) {

        if (getRedisLock(lockKey, "initTaskExecForRedis" + "_" + System.currentTimeMillis())) {
            try {
                if ("key".equals(redisDataType)) {
                    redisTemplate.delete(key);
                }

                if ("list".equals(redisDataType)) {
                    long size = redisTemplate.opsForList().size(key);
                    redisTemplate.opsForList().remove(key, size, null);

                }

                log.info("delRedis method finish@lockKey->" + lockKey + ";key->" + key);

            } finally {
                unRedisLock(lockKey);
            }
        }

        return true;
    }

    /**
     * 通过 数据库 获取 taskExec对象 待执行对象
     *
     * @param batchSysCode 跑批系统代码
     * @param taskGroup    跑批组id
     * @param execDate     跑批执行日期
     * @param taskExecid   跑批执行id
     * @return 待执行 taskExec 对象
     * @throws TaskExecException 跑批异常对象
     */
    @NotNull
    private BatchClearBasicTaskExec getClearBasicTaskExecByDatabase(String batchSysCode, String taskGroup, String execDate, String taskExecid) throws TaskExecException {
        initREDIS_VALUE_EXPIRE(false);
        if ((StringUtils.hasLength(batchSysCode) && StringUtils.hasLength(taskGroup) && StringUtils.hasLength(execDate) && StringUtils.hasLength(taskExecid))) {
            log.info("request 请求参数异常！batchSysCode or taskGroup or snatchingMode or execDate taskExecid is null");
            throw new TaskExecException("request 请求参数异常！batchSysCode or taskGroup or snatchingMode or execDate taskExecid is null",
                    TaskStatusCodeEnum.TASK_INIT_ERROR, "");
        }
        BatchClearBasicTaskExec taskExec;
        LambdaQueryWrapper<BatchClearBasicTaskExec> query_where = new LambdaQueryWrapper<BatchClearBasicTaskExec>()
                .eq(BatchClearBasicTaskExec::getBatchSysCode, batchSysCode)
                .eq(BatchClearBasicTaskExec::getTaskGroup, taskGroup)
                .eq(BatchClearBasicTaskExec::getExecDate, execDate)
                .in(BatchClearBasicTaskExec::getExecStatus, "0", "1", "2", "4")// 执行状态 (0-未执行; 1-占用中; 2-执行中;   3-执行成功; 4-执行失败; 5-跳过执行; 6-任务终止; )
                .orderBy(true, false, BatchClearBasicTaskExec::getExecStatus)
                .orderBy(true, false, BatchClearBasicTaskExec::getExecOrder);

        if (!"".equals(taskExecid)) {
            query_where.eq(BatchClearBasicTaskExec::getTaskExecid, taskExecid);
        }

        List<BatchClearBasicTaskExec> taskExecList = checkTaskExecIsEmpty(clearBasicTaskExecService.list(query_where));

        taskExecList = taskExecRunningOverTimeGC(taskExecList, "database");

        List<BatchClearBasicTaskExec> availableTaskExec = getAvailableTaskExec(taskExecList);
        taskExec = getClearBasicTaskExecByAvailableTaskExec(availableTaskExec);

        if (taskExec == null)
            throw new TaskExecException("抢夺失败未获取可执行步骤！", TaskStatusCodeEnum.TASK_GETTASK_NULL_SUCCESS, "");

        insRedis(
                getGlobalLockKey(taskExec.getBatchSysCode(), taskExec.getTaskGroup(), taskExec.getExecDate()),
                getTaskLockKey(taskExec.getBatchSysCode(), taskExec.getTaskGroup(), taskExec.getExecDate(), taskExec.getTaskExecid()),
                System.currentTimeMillis(),
                REDIS_VALUE_EXPIRE,
                REDIS_VALUE_EXPIRE_TIMEUNIT);

        return taskExec;
    }

    /**
     * 确认 当前列表 是否存在可执行 taskExec 可执行
     *
     * @param list 包含 执行状态 (0-未执行; 1-占用中; 2-执行中;   3-执行成功; 4-执行失败; 5-跳过执行; 6-任务终止; )
     * @throws TaskExecException 跑批异常对象
     */
    private List<BatchClearBasicTaskExec> checkTaskExecIsEmpty(List<BatchClearBasicTaskExec> list) throws TaskExecException {
        int execStatus_0_size = (int) list.stream().filter(item -> item.getExecStatus().equals("0")).count();
        int execStatus_1_size = (int) list.stream().filter(item -> item.getExecStatus().equals("1")).count();
        int execStatus_2_size = (int) list.stream().filter(item -> item.getExecStatus().equals("2")).count();
        // 判断 是否 存在 待执行任务 执行状态(0-未执行; 1-占用中; 2-执行中; )
        if (execStatus_0_size == 0 && execStatus_1_size == 0 && execStatus_2_size == 0) {
            // redisTemplate.delete("task_running_" + list.get(0).getTaskGroup() + "_" + list.get(0).getExecDate());
            log.info("[redisTemplate redis]释放跑批状态锁成功");
            throw new TaskExecException("抢夺完成已无可抢夺任务！", TaskStatusCodeEnum.TASK_GETTASK_NULL_SUCCESS, "");

        }
        return list;
    }

    private BatchClearBasicTaskExec getClearBasicTaskExecByAvailableTaskExec(List<BatchClearBasicTaskExec> availableTaskExec) {
        BatchClearBasicTaskExec taskExec = null;
        for (BatchClearBasicTaskExec obj : availableTaskExec) {
            obj.setExecStatus("1");
            boolean updateResult;
            try {
                updateResult = clearBasicTaskExecService.updateByObject(obj);
            } catch (Exception e) {
                log.info("batchClearBasicTaskExecService updateByObject:" + obj.getTaskExecid());
                continue;
            }
            if (updateResult) {

                // 争夺成功
                taskExec = obj;
                break;
            }
        }

        return taskExec;
    }


    // 初始化跑批数据项目初始化

    public boolean initTask(BatchExecReq req) throws TaskExecException {
        boolean returnValue = false;
        switch (req.getSnatchingMode().toLowerCase(Locale.ROOT)) {
            case "redis":
                returnValue = initTaskExecForDatabase(req.getBatchSysCode(), req.getExecDate(), req.getTaskGroup());
                returnValue = initTaskExecForRedis(req.getBatchSysCode(), req.getExecDate(), req.getTaskGroup()) && returnValue;
                break;
            case "database":
                returnValue = initTaskExecForDatabase(req.getBatchSysCode(), req.getExecDate(), req.getTaskGroup());
                break;
            default:
                break;
        }

        return returnValue;
    }

    /**
     * 初始化 批量处理 清算任务及子步骤信息-redis
     *
     * @param execDate  执行日期
     * @param taskGroup 清算组代码
     * @return 初始化结果 true:成功 false:失败
     * @throws TaskExecException 跑批异常对象
     */
    private boolean initTaskExecForRedis(String batchSysCode, String execDate, String taskGroup) throws TaskExecException {
        log.info("initTaskExecForRedis method@batchSysCode -> " + batchSysCode + "; execDate -> " + execDate + "; taskGroup -> " + taskGroup);
        initREDIS_VALUE_EXPIRE(false);
        if ((!StringUtils.hasLength(batchSysCode) && !StringUtils.hasLength(taskGroup) && !StringUtils.hasLength(execDate))) {
            log.info("request 请求参数异常！batchSysCode or taskGroup or snatchingMode or execDate taskExecid is null");
            throw new TaskExecException("request 请求参数异常！batchSysCode or taskGroup or snatchingMode or execDate taskExecid is null",
                    TaskStatusCodeEnum.TASK_INIT_ERROR, "");
        }
        // 获取 PmcsClearBasicTaskExec 列表
        List<BatchClearBasicTaskExec> taskList = clearBasicTaskExecService.list(new LambdaQueryWrapper<BatchClearBasicTaskExec>()
                .eq(BatchClearBasicTaskExec::getBatchSysCode, batchSysCode)
                .eq(BatchClearBasicTaskExec::getTaskGroup, taskGroup)
                .eq(BatchClearBasicTaskExec::getExecDate, execDate)
                .eq(BatchClearBasicTaskExec::getExecStatus, "0")
                .orderByAsc(BatchClearBasicTaskExec::getExecOrder));
        if (taskList.isEmpty()) {
            return false;
        }
        // 初始化 redis
        String lockKey = getGlobalLockKey(batchSysCode, taskGroup, execDate);
        String listKey = getTaskListKey(batchSysCode, taskGroup, execDate);
        // 1、task_all_list_[execDate] -> 所有可执行任务
        if (getRedisLock(lockKey, "initTaskExecForRedis" + "_" + System.currentTimeMillis())) {
            try {
                if (Boolean.TRUE.equals(redisTemplate.hasKey(listKey))) {
                    redisTemplate.delete(listKey);
                }
                redisTemplate.opsForList().rightPushAll(listKey, taskList);
                redisTemplate.boundValueOps(listKey).expire(REDIS_VALUE_EXPIRE, REDIS_VALUE_EXPIRE_TIMEUNIT);
                log.info("initTaskExecForRedis method finish@batchSysCode -> " + batchSysCode + "; execDate -> " + execDate + "; taskGroup -> " + taskGroup);
            } finally {
                unRedisLock(lockKey);
            }
        }

        return true;
    }

    /**
     * 初始化 批量处理 清算任务及子步骤信息-数据库表
     *
     * @param execDate  执行日期
     * @param taskGroup 清算组代码
     * @return 初始化结果 true:成功 false:失败
     * @throws TaskExecException 跑批异常对象
     */
    public boolean initTaskExecForDatabase(String batchSysCode, String execDate, String taskGroup) throws TaskExecException {
        log.info("initTaskExecForDatabase method@batchSysCode -> " + batchSysCode + "; execDate -> " + execDate + "; taskGroup -> " + taskGroup);

        if ("".equals(batchSysCode) && "".equals(execDate) && "".equals(taskGroup))
            throw new TaskExecException("输入参数异常，batchSysCode、execDate、taskGroup 不能为空！", TaskStatusCodeEnum.TASK_INIT_ERROR, "");

        // 验证 对应taskgroup 分组 、execDate 执行日期是否存在 清算任务配置执行表
        // PmcsClearBasicTaskExec query = clearBasicTaskExecService.getOne(new LambdaQueryWrapper<PmcsClearBasicTaskExec>().eq
        // (PmcsClearBasicTaskExec::getExecDate, execDate).eq(PmcsClearBasicTaskExec::getTaskGroup, taskGroup));

        long query_count =
                clearBasicTaskExecService.count(new LambdaQueryWrapper<BatchClearBasicTaskExec>().eq(BatchClearBasicTaskExec::getBatchSysCode,
                        batchSysCode).eq(BatchClearBasicTaskExec::getExecDate, execDate).eq(BatchClearBasicTaskExec::getTaskGroup, taskGroup));
        if (query_count > 0) {
            log.info("batchSysCode[" + batchSysCode + "] execDate[" + execDate + "] taskGroup[" + taskGroup + "] 批量任务已存在！");
            return true;
        }

        // throw new TaskExecException("execDate[" + execDate + "] taskGroup[" + taskGroup + "] 批量任务已存在！", TaskStatusCodeEnum.TASK_INIT_ERROR, "");

        // 验证 对应taskgroup 分组 、execData 执行日期是否存在 清算任务子步骤配置执行表
        long query_taskStep =
                clearBasicTaskStepExecService.count(new LambdaQueryWrapper<BatchClearBasicTaskStepExec>().eq(BatchClearBasicTaskStepExec::getBatchSysCode, batchSysCode).eq(BatchClearBasicTaskStepExec::getExecDate, execDate).eq(BatchClearBasicTaskStepExec::getTaskGroup, taskGroup));
        if (query_taskStep > 0) {
            // 存在 异常 清算任务子步骤配置执行表
            clearBasicTaskStepExecService.remove(new LambdaQueryWrapper<BatchClearBasicTaskStepExec>().eq(BatchClearBasicTaskStepExec::getBatchSysCode, batchSysCode).eq(BatchClearBasicTaskStepExec::getExecDate, execDate).eq(BatchClearBasicTaskStepExec::getTaskGroup, taskGroup));
            log.info("清理 异常清算任务子步骤配置执行表 [batchSysCode:" + batchSysCode + "、execDate:" + execDate + "、taskGroup:" + taskGroup + "]");
        }

        // 初始化 清算任务配置执行表
        // 初始化 taskexecid
        List<BatchClearBasicTaskSet> clearBasicTaskSetList = clearBasicTaskSetService.list(new LambdaQueryWrapper<BatchClearBasicTaskSet>()
                .eq(BatchClearBasicTaskSet::getBatchSysCode, batchSysCode)
                .eq(BatchClearBasicTaskSet::getTaskGroup, taskGroup)
                // 增加对任务 是否跳过进行过滤
                .eq(BatchClearBasicTaskSet::getCanSkip, "0")
                .orderByAsc(BatchClearBasicTaskSet::getExecOrder));
        // String taskExecId,  String execDate,String execDate


        Collection<BatchClearBasicTaskExec> clearBasicTaskExecsList = new ArrayList<>();
        for (BatchClearBasicTaskSet batchClearBasicTaskSet : clearBasicTaskSetList) {
            String taskExecid = taskBuilder.taskExecId(batchSysCode, taskGroup, batchClearBasicTaskSet.getTaskId(), execDate);
            // pmcsClearBasicTaskExecsList.add(PmcsClearBasicTaskExecForBatch.fromPmcsClearBasicTaskSet(pmcsClearBasicTaskSetList.get(i),taskExecId,
            // execDate,parameterUtil.getParameterByCode(ParamConstant.MWBP_TRANS_DAY)) );
            Map<String, Object> excludepara = new HashMap<String, Object>() {{
                put("batchSysCode", batchSysCode);
                put("taskExecid", taskExecid);
                put("execDate", execDate);
                put("execStatus", "0");
                put("taskDate", parameterUtil.getParameterByCode(CommonConstant.MWBP_TRANS_DAY));

            }};

            clearBasicTaskExecsList.add(ConvertToUtil.convertToV(new BatchClearBasicTaskExec(), batchClearBasicTaskSet, excludepara));

        }

        clearBasicTaskExecService.saveBatchs(clearBasicTaskExecsList);

        List<BatchClearBasicTaskStepSet> clearBasicTaskStepSetList =
                clearBasicTaskStepSetService.list(new LambdaQueryWrapper<BatchClearBasicTaskStepSet>().eq(BatchClearBasicTaskStepSet::getBatchSysCode, batchSysCode).eq(BatchClearBasicTaskStepSet::getTaskGroup, taskGroup));
        Collection<BatchClearBasicTaskStepExec> clearBasicTaskStepExecsList = new ArrayList<>();

        for (int i = 0; i < clearBasicTaskStepSetList.size(); i++) {
            // wuwei@20230511 在创建子任务时 联动 父任务设置参数
            // can_skip
            int finalI = i;
            List<BatchClearBasicTaskExec> taskExecList =
                    clearBasicTaskExecsList.stream().filter(item -> item.getTaskId().equals(clearBasicTaskStepSetList.get(finalI).getTaskId())).collect(Collectors.toList());
            if (taskExecList.size() != 1) {
                // 验证 是 信息缺失还是 设置跳过
                BatchClearBasicTaskSet obj = clearBasicTaskSetService.getOne(
                        new LambdaQueryWrapper<BatchClearBasicTaskSet>()
                                .eq(BatchClearBasicTaskSet::getBatchSysCode, clearBasicTaskStepSetList.get(finalI).getBatchSysCode())
                                .eq(BatchClearBasicTaskSet::getTaskId, clearBasicTaskStepSetList.get(finalI).getTaskId())
                );
                // 存在 配置信息 且 设置为跳过
                if (Objects.nonNull(obj) && !obj.getCanSkip().equals("0")) {
                    continue;
                } else {
                    throw new TaskExecException("子任务获取父任务列表失败！参数：taskId:" + clearBasicTaskStepSetList.get(finalI).getTaskId(),
                            TaskStatusCodeEnum.TASK_ERROR, "");
                }
            }
            if (!taskExecList.get(0).getCanSkip().equals("0")) {
                clearBasicTaskStepSetList.get(finalI).setIsSkip(taskExecList.get(0).getCanSkip());
            }


            String taskExecid = taskBuilder.taskExecId(clearBasicTaskStepSetList.get(finalI).getBatchSysCode(), taskGroup,
                    clearBasicTaskStepSetList.get(i).getTaskId(), clearBasicTaskStepSetList.get(i).getStepNo(), execDate);
            Map<String, Object> excludepara = new HashMap<String, Object>() {{
                put("serverIp", "");
                put("targetCode", "");
                put("rtnCode", "");
                put("execStatus", "0");
                put("rtnDesc", "");
                put("serverName", "");
                put("threadUuid", "");
                put("taskExecid", taskExecid);
                put("execDate", execDate);
                put("taskDate", parameterUtil.getParameterByCode(CommonConstant.MWBP_TRANS_DAY));
                put("shouldExecDate", execDate);
            }};

            clearBasicTaskStepExecsList.add(ConvertToUtil.convertToV(new BatchClearBasicTaskStepExec(), clearBasicTaskStepSetList.get(i),
                    excludepara));
        }

        clearBasicTaskStepExecService.saveBatchs(clearBasicTaskStepExecsList);

        log.info("initTaskExec method finish@batchSysCode -> " + batchSysCode + ";execDate -> " + execDate + ";taskGroup -> " + taskGroup);


        return true;
    }

    /**
     * 贵金属批量处理-重置跑批任务 根据 request 获取 execDate、taskGroup 对已生成数据进行删除操作后进行申请
     */
    public boolean resetTask(BatchExecReq request) throws TaskExecException {
        String execDate = request.getExecDate();
        String taskGroup = request.getTaskGroup();
        String batchSysCode = request.getBatchSysCode();
        if (!(StringUtils.hasLength(request.getBatchSysCode()) && StringUtils.hasLength(request.getTaskGroup()) && StringUtils.hasLength(request.getExecDate()))) {
            log.info("request 请求参数异常！batchSysCode or taskGroup or snatchingMode or taskDate is null");
            return false;
        }

        log.info("resetTask method@batchSysCode -> " + batchSysCode + ";execDate -> " + execDate + ";taskGroup -> " + taskGroup);


        // 生成前先进行删除备份
        List<BatchClearBasicTaskExec> tasklist = clearBasicTaskExecService.list(new LambdaQueryWrapper<BatchClearBasicTaskExec>()
                .eq(BatchClearBasicTaskExec::getBatchSysCode, batchSysCode)
                .eq(BatchClearBasicTaskExec::getTaskGroup, taskGroup)
                .eq(BatchClearBasicTaskExec::getExecDate, execDate));
        List<BatchClearBasicTaskStepExec> taskSteplist = clearBasicTaskStepExecService.list(new LambdaQueryWrapper<BatchClearBasicTaskStepExec>()
                .eq(BatchClearBasicTaskStepExec::getBatchSysCode, batchSysCode)
                .eq(BatchClearBasicTaskStepExec::getTaskGroup, taskGroup)
                .eq(BatchClearBasicTaskStepExec::getExecDate, execDate));

        if (!tasklist.isEmpty()) {
            // 生成备份信息
            List<BatchClearBasicTaskExecDel> taskDelist = new ArrayList<>();
            for (BatchClearBasicTaskExec task : tasklist) {
                BatchClearBasicTaskExecDel taskDel = new BatchClearBasicTaskExecDel();
                ConvertToUtil.convertToV(taskDel, task, new HashMap<String, Object>() {{
                    put("delId", task.getId());
                }});
                // taskDel.setId(null);
                taskDelist.add(taskDel);
            }
            List<BatchClearBasicTaskStepExecDel> taskStepDelist = new ArrayList<>();
            for (BatchClearBasicTaskStepExec taskStep : taskSteplist) {
                BatchClearBasicTaskStepExecDel taskStepDel = new BatchClearBasicTaskStepExecDel();
                ConvertToUtil.convertToV(taskStepDel, taskStep, new HashMap<String, Object>() {
                    {
                        put("delId", taskStep.getId());
                    }
                });
                // taskStepDel.setId(null);
                taskStepDelist.add(taskStepDel);
            }

            // 数据持久化备份数据
            clearBasicTaskExecDelService.saveBatchs(taskDelist);
            clearBasicTaskStepExecDelService.saveBatchs(taskStepDelist);
//            clearBasicTaskExecDelService.updateBatchById(taskDelist);
//            clearBasicTaskStepExecDelService.updateBatchById(taskStepDelist);

            // 删除已生成数据
            clearBasicTaskExecService.removeBatch(tasklist);
            clearBasicTaskStepExecService.removeBatch(taskSteplist);
        }
        return initTask(request);
    }


    /**
     * 前序条件 跑批任务状态为停止
     * 通过 execId 执行特定任务
     *
     * @param taskGroup     跑批组id
     * @param execDate      跑批执行日期
     * @param taskExecid    跑批执行id
     * @param snatchingMode 跑批抢夺模式
     * @return 跑批对象
     * @throws TaskExecException 跑批异常对象
     */
    public BatchClearBasicTaskExec doTaskExecByTaskExecId(String batchSysCode, String taskGroup, String execDate, String taskExecid,
                                                          String snatchingMode) throws TaskExecException {

        // 更新 database
        List<BatchClearBasicTaskExec> taskExecList = clearBasicTaskExecService.list(new LambdaQueryWrapper<BatchClearBasicTaskExec>()
                .eq(BatchClearBasicTaskExec::getBatchSysCode, batchSysCode)
                .eq(BatchClearBasicTaskExec::getTaskExecid, taskExecid));
        BatchClearBasicTaskExec obj;
        if (taskExecList.isEmpty()) {
            throw new TaskExecException("doTaskExec -> 获取 PmcsClearBasicTaskExec 失败！未查询到 task_exec_id [" + taskExecid + "] 的信息",
                    TaskStatusCodeEnum.TASK_RUNNING_RUNNINGERROR, taskExecid);
        } else {
            obj = taskExecList.get(0);
        }
        obj.setExecStatus("2");
        try {
            clearBasicTaskExecService.updateByObject(obj);
        } catch (Exception e) {
            String errMsg = "BatchClearBasicTaskExecService updateByObject:" + obj.getTaskExecid();
            throw new TaskExecException(errMsg, TaskStatusCodeEnum.TASK_RUNNING_RUNNINGERROR, taskExecid);
        }
        // 更新子任务执行状态
        try {
            clearBasicTaskStepExecService.updateTaskStepExecForInit(obj.getBatchSysCode(), obj.getTaskGroup(), obj.getTaskId(), obj.getExecDate());
        } catch (Exception e) {
            String errMsg = obj.getTaskGroup() + " " + obj.getTaskId() + " " + obj.getExecDate();
            throw new TaskExecException(errMsg, TaskStatusCodeEnum.TASK_RUNNING_RUNNINGERROR, taskExecid);

        }

        if (snatchingMode.equals("redis")) {
            // 更新 redis 执行列表 对象状态
            updateRedisListValue(obj);
        }
        // 删除 redis
        if (Boolean.TRUE.equals(redisTemplate.hasKey(getTaskLockKey(batchSysCode, taskGroup, execDate, taskExecid)))) {
            delRedis(getGlobalLockKey(batchSysCode, taskGroup, execDate), getTaskLockKey(batchSysCode, taskGroup, execDate, taskExecid));
        }
        return obj;
    }

    public void startTaskRunning(String batchSysCode, String taskGroup, String execDate) throws TaskExecException {
        if (Boolean.TRUE.equals(redisTemplate.hasKey(getTaskRunningKey(batchSysCode, taskGroup, execDate)))) {
            throw new TaskExecException("startTaskRunning 已存在 task_running_" + taskGroup + "_" + execDate + " [redis]！ ",
                    TaskStatusCodeEnum.TASK_ERROR, "");
        }
        initREDIS_VALUE_EXPIRE(false);

        if (!insRedis(
                getGlobalLockKey(batchSysCode, taskGroup, execDate),
                getTaskRunningKey(batchSysCode, taskGroup, execDate),
                System.currentTimeMillis(),
                REDIS_VALUE_EXPIRE,
                REDIS_TASK_LIST_EXPIRE_TIMEUNIT)) {
            throw new TaskExecException("startTaskRunning 创建 task_running_" + batchSysCode + "_" + taskGroup + "_" + execDate + " [redis] 失败！ ",
                    TaskStatusCodeEnum.TASK_ERROR, "");

        }


    }

    public void stopTaskRunning(String batchSysCode, String taskGroup, String execDate) throws TaskExecException {
        if (Boolean.TRUE.equals(redisTemplate.hasKey(getTaskRunningKey(batchSysCode, taskGroup, execDate)))) {
            delRedis(getGlobalLockKey(batchSysCode, taskGroup, execDate), getTaskRunningKey(batchSysCode, taskGroup, execDate));
        }

    }

    /**
     * 日间跑批任务 强制停止 redis 缓存 未获取标志 抛出异常
     */
    public void stopTaskDailyRunning(String batchSysCode, String taskPubId, String taskExecid) throws TaskDailyException {

    }

    /**
     * 验证跑批日期有效性
     *
     * @param execDate 跑批日期
     * @throws TaskExecException 跑批异常对象
     */
    public void checkExecDate(String execDate) throws TaskExecException {

        // 增加 全局参数 控制 当前限制 是否有效
        if (!Boolean.parseBoolean(parameterUtil.getParameterByCode(CommonConstant.IS_CHECK_EXEC_DATE))) {
            return;
        }

        if (!StringUtils.hasLength(execDate)) {
            throw new TaskExecException("execDate 不能为空！", TaskStatusCodeEnum.TASK_ERROR, "");
        }

        // 验证 日期格式是否合法
        if (!ConvertToUtil.isValidDate(execDate, "yyyyMMdd")) {
            throw new TaskExecException("execDate[" + execDate + "] 格式不合法！", TaskStatusCodeEnum.TASK_ERROR, "");
        }

        // 验证 当前日期 小于大于
        String taskExecLastDate = taskBuilder.getTaskExecDate();
        if (execDate.compareTo(taskExecLastDate) < 0) {
            throw new TaskExecException("execDate[" + execDate + "] 不合法！数据日期应大于或等于当前系统上一工作日[" + taskExecLastDate + "]",
                    TaskStatusCodeEnum.TASK_ERROR, "");
        }

    }

    /**
     * 验证日间跑批日期有效性
     *
     * @param execDate 跑批日期
     * @throws TaskDailyException 日间跑批异常对象
     */
    public void checkDailyExecDate(String execDate) throws TaskDailyException {

        // 增加 全局参数 控制 当前限制 是否有效
        if (!Boolean.parseBoolean(parameterUtil.getParameterByCode(CommonConstant.IS_CHECK_EXEC_DATE))) {
            return;
        }

        if (!StringUtils.hasLength(execDate)) {
            throw new TaskDailyException("execDate 不能为空！", TaskDailyStatusCodeEnum.TASK_ERROR, "");
        }

        // 验证 日期格式是否合法
        if (!ConvertToUtil.isValidDate(execDate, "yyyyMMdd")) {
            throw new TaskDailyException("execDate[" + execDate + "] 格式不合法！", TaskDailyStatusCodeEnum.TASK_ERROR, "");
        }

        // 验证 当前日期 小于大于
        String taskExecLastDate = taskBuilder.getTaskExecDate();
        if (execDate.compareTo(taskExecLastDate) < 0) {
            throw new TaskDailyException("execDate[" + execDate + "] 不合法！数据日期应大于或等于当前系统上一工作日[" + taskExecLastDate + "]",
                    TaskDailyStatusCodeEnum.TASK_ERROR, "");
        }

    }


    /**
     * 初始化 batchExecReq 对象
     */
    public BatchExecReq initBatchExecReq(BatchExecReq request) throws TaskExecException {
        if (!(StringUtils.hasLength(request.getBatchSysCode()) && StringUtils.hasLength(request.getTaskGroup()) && StringUtils.hasLength(request.getExecDate()))) {
            log.info("request 请求参数异常！batchSysCode or taskGroup or snatchingMode or taskDate is null");
            throw new TaskExecException("request 请求参数异常！batchSysCode or taskGroup or snatchingMode or taskDate is null",
                    TaskStatusCodeEnum.TASK_ERROR, "");
        }

        // 根据 request taskExecId 获取 pmcsClearBasicTaskExec 对象
        BatchClearBasicTaskExec taskExec =
                clearBasicTaskExecService.getOne(new LambdaQueryWrapper<BatchClearBasicTaskExec>().eq(BatchClearBasicTaskExec::getBatchSysCode,
                        request.getBatchSysCode()).eq(BatchClearBasicTaskExec::getTaskExecid, request.getTaskExecId()));
        if (taskExec == null) {
            throw new TaskExecException("taskExecId[" + request.getTaskExecId() + "] 不合法！未查询到batchClearBasicTaskExec对象",
                    TaskStatusCodeEnum.TASK_ERROR, "");
        }
        request.setTaskGroup(taskExec.getTaskGroup());
        request.setTaskId(taskExec.getTaskId());
        request.setExecDate(taskExec.getExecDate());
        request.setSliceServiceClass(taskExec.getSliceServiceClass());
        request.setBatchSysCode(taskExec.getBatchSysCode());
        return request;
    }

    public BatchDailyExecReq initBatchExecReq(BatchDailyExecReq request) throws TaskDailyException {
        if (!(StringUtils.hasLength(request.getTaskExecid()))) {
            log.info("request 请求参数异常！taskExecid is null");
            throw new TaskDailyException("request 请求参数异常！taskExecid is null",
                    TaskDailyStatusCodeEnum.TASK_ERROR, "");
        }

        // 根据 request taskExecId 获取 batch_daily_basic_task_step_exec 对象
        BatchDailyBasicTaskStepExec taskDailyExec =
                dailyBasicTaskStepExecService.getOne(new LambdaQueryWrapper<BatchDailyBasicTaskStepExec>().eq(BatchDailyBasicTaskStepExec::getTaskExecid, request.getTaskExecid())
                        .eq(BatchDailyBasicTaskStepExec::getExecStatus, "1"));
        if (taskDailyExec == null) {
            throw new TaskDailyException("taskExecId[" + request.getTaskExecid() + "] 不合法！未查询到batch_daily_basic_task_step_exec execStatus = 1 的对象",
                    TaskDailyStatusCodeEnum.TASK_ERROR, "");
        }
        request.setExecDate(taskDailyExec.getExecDate());
        request.setSliceServiceClass(taskDailyExec.getSliceServiceClass());
        request.setSliceServiceBaseClass(taskDailyExec.getSliceServiceBaseClass());
        request.setBatchSysCode(taskDailyExec.getBatchSysCode());
        request.setTaskDailyExec(taskDailyExec);
        request.setTaskPubId(taskDailyExec.getTaskPubId());

        BatchDailyBasicTaskPublish taskPublishObj =
                dailyBasicTaskPublishService.getOne(new LambdaQueryWrapper<BatchDailyBasicTaskPublish>()
                        .eq(BatchDailyBasicTaskPublish::getTaskPubId, request.getTaskPubId())
                        .eq(BatchDailyBasicTaskPublish::getTaskPubStauts, EnumUtil.日间跑批任务请求表_发布状态_发布中));
        if (taskPublishObj == null) {
            throw new TaskDailyException("request 请求参数异常！ taskPubId 参数[" + request.getTaskPubId() +
                    "]未获取有效待执行跑批日间任务请求表[batch_daily_basic_task_publish]", TaskDailyStatusCodeEnum.TASK_ERROR, "");
        }
        request.setTaskPublishObj(taskPublishObj);
        request.setBatchSysCode(XssFilterUtils.filterXss(taskPublishObj.getBatchSysCode()));
        return request;
    }

    //public boolean optTradeCapitalCheckError(String errorSerno, String optDealStatus, String optDealSummary) {
    //    // 验证参数有效性
    //    // 获取资金流水差错信息
    //    List<MwbpTradeCapitalCheckError> tradeCapitalCheckErrorList =
    //            tradeCapitalCheckErrorService.list(new LambdaQueryWrapper<MwbpTradeCapitalCheckError>().eq(MwbpTradeCapitalCheckError::getErrorSerno, errorSerno));
    //    if (tradeCapitalCheckErrorList.isEmpty()) return false;
    //    if (!tradeCapitalCheckErrorList.get(0).getDealStatus().equals(EnumUtil.资金交易对账差错表_差错状态_未处理))
    //        return false;
    //
    //    // 更新 资金流水差错信息 差错状态 0:未处理 -> 3:处理中
    //    // optDealStatus:2 更新 资金流水差错信息  差错状态 0:未处理 -> 2:线下处理
    //    if (optDealStatus.equals(EnumUtil.资金交易对账差错表_差错状态_人工线下处理)) {
    //        tradeCapitalCheckErrorList.get(0).setDealStatus(EnumUtil.资金交易对账差错表_差错状态_人工线下处理);
    //        tradeCapitalCheckErrorList.get(0).setOptDealSummary(optDealSummary);
    //        tradeCapitalCheckErrorService.updateById(tradeCapitalCheckErrorList.get(0));
    //        return true;
    //    }
    //    if (optDealStatus.equals(EnumUtil.资金交易对账差错表_差错状态_人工线上处理)) {
    //        tradeCapitalCheckErrorList.get(0).setDealStatus(EnumUtil.资金交易对账差错表_差错状态_线上处理中);
    //        tradeCapitalCheckErrorService.updateById(tradeCapitalCheckErrorList.get(0));
    //        return true;
    //    }
    //    log.info("TaskExecFactory -> optTradeCapitalCheckError Exception!parameter " + errorSerno + "[errorSerno];" + optDealStatus +
    //            "[optDealStatus];");
    //
    //    return false;
    //
    //}


}
